<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="robots" content="noodp" />
        <meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
        <title>MyBatis Generator 源码笔记 - 负责和大饼的博客</title><meta name="Description" content="想专注于学点手艺"><meta property="og:title" content="MyBatis Generator 源码笔记" />
<meta property="og:description" content="MyBatis Generator 源码笔记 核心功能 入口 org.mybatis.generator.api.ShellRunner#main 主流程 解析args入参，得到命令行的输入参数 通过ConfigurationParser类解析xml配置文件 通过My" />
<meta property="og:type" content="article" />
<meta property="og:url" content="https://amath0312.github.com/mybatis-generator-code-read/" />
<meta property="og:image" content="https://amath0312.github.com/mybatis-generator-code-read/assets/mybatis-superbird-small.png"/>
<meta property="article:published_time" content="2020-10-18T12:42:31+08:00" />
<meta property="article:modified_time" content="2020-10-18T12:42:31+08:00" />
<meta name="twitter:card" content="summary_large_image"/>
<meta name="twitter:image" content="https://amath0312.github.com/mybatis-generator-code-read/assets/mybatis-superbird-small.png"/>
<meta name="twitter:title" content="MyBatis Generator 源码笔记"/>
<meta name="twitter:description" content="MyBatis Generator 源码笔记 核心功能 入口 org.mybatis.generator.api.ShellRunner#main 主流程 解析args入参，得到命令行的输入参数 通过ConfigurationParser类解析xml配置文件 通过My"/>
<meta name="application-name" content="负责和大饼的博客">
<meta name="apple-mobile-web-app-title" content="负责和大饼的博客"><link rel="shortcut icon" type="image/x-icon" href="/favicon.ico" />
        <link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
        <link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png"><link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png"><link rel="manifest" href="/site.webmanifest"><link rel="canonical" href="https://amath0312.github.com/mybatis-generator-code-read/" /><link rel="prev" href="https://amath0312.github.com/install_nexus/" /><link rel="next" href="https://amath0312.github.com/uber-payments-arch/" /><link rel="stylesheet" href="/lib/normalize/normalize.min.css"><link rel="stylesheet" href="/css/style.min.css"><link rel="stylesheet" href="/lib/fontawesome-free/all.min.css"><link rel="stylesheet" href="/lib/animate/animate.min.css"><script type="application/ld+json">
    {
        "@context": "http://schema.org",
        "@type": "BlogPosting",
        "headline": "MyBatis Generator 源码笔记",
        "inLanguage": "zh-CN",
        "mainEntityOfPage": {
            "@type": "WebPage",
            "@id": "https:\/\/amath0312.github.com\/mybatis-generator-code-read\/"
        },"image": [{
                            "@type": "ImageObject",
                            "url": "https:\/\/amath0312.github.com\/mybatis-generator-code-read\/assets\/mybatis-superbird-small.png",
                            "width":  350 ,
                            "height":  88 
                        }],"genre": "posts","keywords": "mybatis, mbg, 源码阅读","wordcount":  3574 ,
        "url": "https:\/\/amath0312.github.com\/mybatis-generator-code-read\/","datePublished": "2020-10-18T12:42:31+08:00","dateModified": "2020-10-18T12:42:31+08:00","publisher": {
            "@type": "Organization",
            "name": "负责和大饼"},"author": {
                "@type": "Person",
                "name": "负责和大饼"
            },"description": ""
    }
    </script></head>
    <body header-desktop="" header-mobile=""><script type="text/javascript">(window.localStorage && localStorage.getItem('theme') ? localStorage.getItem('theme') === 'dark' : ('' === 'auto' ? window.matchMedia('(prefers-color-scheme: dark)').matches : '' === 'dark')) && document.body.setAttribute('theme', 'dark');</script>

        <div id="mask"></div><div class="wrapper"><header class="desktop" id="header-desktop">
    <div class="header-wrapper">
        <div class="header-title">
            <a href="/" title="负责和大饼的博客">负责和大饼的博客</a>
        </div>
        <div class="menu">
            <div class="menu-inner"><a class="menu-item" href="/posts/"> Blog </a><a class="menu-item" href="/categories/"> Categories </a><a class="menu-item" href="/tags/"> Tags </a><a class="menu-item" href="/about/"> About </a><span class="menu-item delimiter"></span><span class="menu-item search" id="search-desktop">
                        <input type="text" placeholder="Search titles or contents..." id="search-input-desktop">
                        <a href="javascript:void(0);" class="search-button search-toggle" id="search-toggle-desktop" title="Search">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="javascript:void(0);" class="search-button search-clear" id="search-clear-desktop" title="Clear">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-desktop">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </span><a href="javascript:void(0);" class="menu-item theme-switch" title="Switch Theme">
                    <i class="fas fa-adjust fa-fw"></i>
                </a>
            </div>
        </div>
    </div>
</header><header class="mobile" id="header-mobile">
    <div class="header-container">
        <div class="header-wrapper">
            <div class="header-title">
                <a href="/" title="负责和大饼的博客">负责和大饼的博客</a>
            </div>
            <div class="menu-toggle" id="menu-toggle-mobile">
                <span></span><span></span><span></span>
            </div>
        </div>
        <div class="menu" id="menu-mobile"><div class="search-wrapper">
                    <div class="search mobile" id="search-mobile">
                        <input type="text" placeholder="Search titles or contents..." id="search-input-mobile">
                        <a href="javascript:void(0);" class="search-button search-toggle" id="search-toggle-mobile" title="Search">
                            <i class="fas fa-search fa-fw"></i>
                        </a>
                        <a href="javascript:void(0);" class="search-button search-clear" id="search-clear-mobile" title="Clear">
                            <i class="fas fa-times-circle fa-fw"></i>
                        </a>
                        <span class="search-button search-loading" id="search-loading-mobile">
                            <i class="fas fa-spinner fa-fw fa-spin"></i>
                        </span>
                    </div>
                    <a href="javascript:void(0);" class="search-cancel" id="search-cancel-mobile">
                        Cancel
                    </a>
                </div><a class="menu-item" href="/posts/" title="">Blog</a><a class="menu-item" href="/categories/" title="">Categories</a><a class="menu-item" href="/tags/" title="">Tags</a><a class="menu-item" href="/about/" title="">About</a><a href="javascript:void(0);" class="menu-item theme-switch" title="Switch Theme">
                <i class="fas fa-adjust fa-fw"></i>
            </a></div>
    </div>
</header>
<div class="search-dropdown desktop">
    <div id="search-dropdown-desktop"></div>
</div>
<div class="search-dropdown mobile">
    <div id="search-dropdown-mobile"></div>
</div>
<main class="main">
                <div class="container"><div class="toc" id="toc-auto">
            <h2 class="toc-title">Contents</h2>
            <div class="toc-content always-active" id="toc-content-auto"></div>
        </div><article class="page single"><h1 class="single-title animated flipInX">MyBatis Generator 源码笔记</h1><div class="post-meta">
            <div class="post-meta-line"><span class="post-author"><a href="/" title="Author" rel=" author" class="author"><i class="fas fa-user-circle fa-fw"></i>负责和大饼</a></span>&nbsp;<span class="post-category">included in <a href="/categories/dev/"><i class="far fa-folder fa-fw"></i>dev</a>&nbsp;<a href="/categories/mybatis/"><i class="far fa-folder fa-fw"></i>mybatis</a></span></div>
            <div class="post-meta-line"><i class="far fa-calendar-alt fa-fw"></i>&nbsp;<time datetime="2020-10-08">2020-10-08</time>&nbsp;<i class="fas fa-pencil-alt fa-fw"></i>&nbsp;3574 words&nbsp;
                <i class="far fa-clock fa-fw"></i>&nbsp;8 minutes&nbsp;</div>
        </div><div class="featured-image"><img
        class="lazyload"
        src="/svg/loading.min.svg"
        data-src="/mybatis-generator-code-read/assets/mybatis-superbird-small.png"
        data-srcset="/mybatis-generator-code-read/assets/mybatis-superbird-small.png, /mybatis-generator-code-read/assets/mybatis-superbird-small.png 1.5x, /mybatis-generator-code-read/assets/mybatis-superbird-small.png 2x"
        data-sizes="auto"
        alt="/mybatis-generator-code-read/assets/mybatis-superbird-small.png"
        title="/mybatis-generator-code-read/assets/mybatis-superbird-small.png" /></div><div class="details toc" id="toc-static"  kept="">
                <div class="details-summary toc-title">
                    <span>Contents</span>
                    <span><i class="details-icon fas fa-angle-right"></i></span>
                </div>
                <div class="details-content toc-content" id="toc-content-static"><nav id="TableOfContents">
  <ul>
    <li><a href="#核心功能">核心功能</a>
      <ul>
        <li><a href="#入口">入口</a></li>
        <li><a href="#解析配置文件">解析配置文件</a></li>
        <li><a href="#生成代码">生成代码</a></li>
      </ul>
    </li>
    <li><a href="#maven-wrapper">maven wrapper</a>
      <ul>
        <li><a href="#安装maven-wrapper">安装maven-wrapper</a></li>
        <li><a href="#使用阿里云镜像">使用阿里云镜像</a></li>
      </ul>
    </li>
  </ul>

  <ul>
    <li><a href="#核心功能-1">核心功能</a>
      <ul>
        <li><a href="#入口-1">入口</a></li>
        <li><a href="#解析配置文件-1">解析配置文件</a></li>
        <li><a href="#生成代码-1">生成代码</a></li>
      </ul>
    </li>
    <li><a href="#maven-wrapper-1">maven wrapper</a>
      <ul>
        <li><a href="#安装maven-wrapper-1">安装maven-wrapper</a></li>
        <li><a href="#使用阿里云镜像-1">使用阿里云镜像</a></li>
      </ul>
    </li>
  </ul>
</nav></div>
            </div><div class="content" id="content"><h1 id="mybatis-generator-源码笔记">MyBatis Generator 源码笔记</h1>
<h2 id="核心功能">核心功能</h2>
<h3 id="入口">入口</h3>
<blockquote>
<p>org.mybatis.generator.api.ShellRunner#main</p>
</blockquote>
<h4 id="主流程">主流程</h4>
<ol>
<li>解析args入参，得到命令行的输入参数</li>
<li>通过<code>ConfigurationParser</code>类解析xml配置文件</li>
<li>通过<code>MyBatisGenerator</code>生成代码</li>
</ol>
<h4 id="知识点">知识点</h4>
<ol>
<li>
<p>可参考<code>parseCommandLine</code>方法，实现args解析的功能</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="kd">private</span> <span class="kd">static</span> <span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">&gt;</span> <span class="nf">parseCommandLine</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">errors</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">&lt;&gt;();</span>
    <span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">&gt;</span> <span class="n">arguments</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HashMap</span><span class="o">&lt;&gt;();</span>

    <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">args</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">CONFIG_FILE</span><span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">]))</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">((</span><span class="n">i</span> <span class="o">+</span> <span class="n">1</span><span class="o">)</span> <span class="o">&lt;</span> <span class="n">args</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">arguments</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">CONFIG_FILE</span><span class="o">,</span> <span class="n">args</span><span class="o">[</span><span class="n">i</span> <span class="o">+</span> <span class="n">1</span><span class="o">]);</span>
            <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
                <span class="n">errors</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">getString</span><span class="o">(</span>
                        <span class="s">&#34;RuntimeError.19&#34;</span><span class="o">,</span> <span class="n">CONFIG_FILE</span><span class="o">));</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>            <span class="o">}</span>
            <span class="n">i</span><span class="o">++;</span>
        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">VERBOSE</span><span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">]))</span> <span class="o">{</span>
            <span class="n">arguments</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">VERBOSE</span><span class="o">,</span> <span class="s">&#34;Y&#34;</span><span class="o">);</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">HELP_1</span><span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">]))</span> <span class="o">{</span>
            <span class="n">arguments</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">HELP_1</span><span class="o">,</span> <span class="s">&#34;Y&#34;</span><span class="o">);</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">FORCE_JAVA_LOGGING</span><span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">]))</span> <span class="o">{</span>
            <span class="n">LogFactory</span><span class="o">.</span><span class="na">forceJavaLogging</span><span class="o">();</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="n">errors</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">getString</span><span class="o">(</span><span class="s">&#34;RuntimeError.20&#34;</span><span class="o">,</span> <span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">]));</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>        <span class="o">}</span>
    <span class="o">}</span>

    <span class="k">if</span> <span class="o">(!</span><span class="n">errors</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="n">String</span> <span class="n">error</span> <span class="o">:</span> <span class="n">errors</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">writeLine</span><span class="o">(</span><span class="n">error</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="n">System</span><span class="o">.</span><span class="na">exit</span><span class="o">(-</span><span class="n">1</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="k">return</span> <span class="n">arguments</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></li>
<li>
<p>掌握<code>StringTokenizer</code>常用方法</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="n">StringTokenizer</span> <span class="n">st</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringTokenizer</span><span class="o">(</span><span class="n">arguments</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">TABLES</span><span class="o">),</span> <span class="s">&#34;,&#34;</span><span class="o">);</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span><span class="k">while</span> <span class="o">(</span><span class="n">st</span><span class="o">.</span><span class="na">hasMoreTokens</span><span class="o">())</span> <span class="o">{</span>
    <span class="n">String</span> <span class="n">s</span> <span class="o">=</span> <span class="n">st</span><span class="o">.</span><span class="na">nextToken</span><span class="o">().</span><span class="na">trim</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">&gt;</span> <span class="n">0</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">fullyqualifiedTables</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">s</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></li>
</ol>
<h3 id="解析配置文件">解析配置文件</h3>
<blockquote>
<p>ConfigurationParser</p>
</blockquote>
<h4 id="主流程-1">主流程</h4>
<ol>
<li>创建<code>ConfigurationParser</code>对象，执行<code>parseConfiguration</code>方法</li>
<li>调用<code>MyBatisGeneratorConfigurationParser#parseConfiguration</code>方法</li>
<li>遍历所有xml-element，对<code>properties</code>、<code>classPathEntry</code>、<code>context</code>分别做解析</li>
</ol>
<h4 id="知识点-1">知识点</h4>
<ol>
<li>
<p>XML解析（进阶应用）:dtd验证xml内容、异常处理</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="n">DocumentBuilderFactory</span> <span class="n">factory</span> <span class="o">=</span> <span class="n">DocumentBuilderFactory</span><span class="o">.</span><span class="na">newInstance</span><span class="o">();</span>
<span class="n">factory</span><span class="o">.</span><span class="na">setValidating</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
<span class="n">factory</span><span class="o">.</span><span class="na">setFeature</span><span class="o">(</span><span class="n">XMLConstants</span><span class="o">.</span><span class="na">FEATURE_SECURE_PROCESSING</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>

<span class="n">DocumentBuilder</span> <span class="n">builder</span> <span class="o">=</span> <span class="n">factory</span><span class="o">.</span><span class="na">newDocumentBuilder</span><span class="o">();</span>
<span class="n">builder</span><span class="o">.</span><span class="na">setEntityResolver</span><span class="o">(</span><span class="k">new</span> <span class="n">ParserEntityResolver</span><span class="o">());</span>

<span class="n">ParserErrorHandler</span> <span class="n">handler</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ParserErrorHandler</span><span class="o">(</span><span class="n">warnings</span><span class="o">,</span>
        <span class="n">parseErrors</span><span class="o">);</span>
<span class="n">builder</span><span class="o">.</span><span class="na">setErrorHandler</span><span class="o">(</span><span class="n">handler</span><span class="o">);</span>

<span class="c1">//use dtd to verify entity
</span><span class="c1"></span><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ParserEntityResolver</span> <span class="kd">implements</span> <span class="n">EntityResolver</span> <span class="o">{</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="n">InputSource</span> <span class="nf">resolveEntity</span><span class="o">(</span><span class="n">String</span> <span class="n">publicId</span><span class="o">,</span> <span class="n">String</span> <span class="n">systemId</span><span class="o">)</span>
            <span class="kd">throws</span> <span class="n">SAXException</span><span class="o">,</span> <span class="n">IOException</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">XmlConstants</span><span class="o">.</span><span class="na">MYBATIS_GENERATOR_CONFIG_PUBLIC_ID</span>
                <span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">publicId</span><span class="o">))</span> <span class="o">{</span>
            <span class="n">InputStream</span> <span class="n">is</span> <span class="o">=</span> <span class="n">getClass</span><span class="o">()</span>
                    <span class="o">.</span><span class="na">getClassLoader</span><span class="o">()</span>
                    <span class="o">.</span><span class="na">getResourceAsStream</span><span class="o">(</span>
                            <span class="s">&#34;org/mybatis/generator/config/xml/mybatis-generator-config_1_0.dtd&#34;</span><span class="o">);</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>            <span class="k">return</span> <span class="k">new</span> <span class="n">InputSource</span><span class="o">(</span><span class="n">is</span><span class="o">);</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></li>
<li>
<p>通过classLoader读取类路径内的资源，通过文件路径（url）读取本地路径的文件</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"> <span class="n">URL</span> <span class="n">resourceUrl</span> <span class="o">=</span> <span class="n">ObjectFactory</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getClassLoader</span><span class="o">().</span><span class="na">getResource</span><span class="o">(</span><span class="n">resource</span><span class="o">);</span>
 <span class="n">URL</span> <span class="n">fileUrl</span> <span class="o">=</span> <span class="k">new</span> <span class="n">URL</span><span class="o">(</span><span class="n">url</span><span class="o">);</span>
</code></pre></div></li>
<li>
<p>解析带有${KEY}的表达式</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="c1">// 代码有所简化，不可直接使用
</span><span class="c1"></span><span class="n">String</span> <span class="nf">parsePropertyTokens</span><span class="o">(</span><span class="n">String</span> <span class="n">s</span><span class="o">)</span> <span class="o">{</span>
    <span class="kd">final</span> <span class="n">String</span> <span class="n">OPEN</span> <span class="o">=</span> <span class="s">&#34;${&#34;</span><span class="o">;</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>    <span class="kd">final</span> <span class="n">String</span> <span class="n">CLOSE</span> <span class="o">=</span> <span class="s">&#34;}&#34;</span><span class="o">;</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>    <span class="kt">int</span> <span class="n">currentIndex</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span>
    <span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">answer</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">&lt;&gt;();</span>

    <span class="kt">int</span> <span class="n">markerStartIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">OPEN</span><span class="o">);</span>
    <span class="k">while</span> <span class="o">(</span><span class="n">markerStartIndex</span> <span class="o">&gt;</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">markerStartIndex</span> <span class="o">&gt;</span> <span class="n">currentIndex</span><span class="o">)</span> <span class="o">{</span>
            <span class="c1">// add the characters before the next parameter marker
</span><span class="c1"></span>            <span class="n">answer</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">currentIndex</span><span class="o">,</span> <span class="n">markerStartIndex</span><span class="o">));</span>
            <span class="n">currentIndex</span> <span class="o">=</span> <span class="n">markerStartIndex</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kt">int</span> <span class="n">markerEndIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">CLOSE</span><span class="o">,</span> <span class="n">currentIndex</span><span class="o">);</span>
        <span class="kt">int</span> <span class="n">nestedStartIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">OPEN</span><span class="o">,</span> <span class="n">markerStartIndex</span> <span class="o">+</span> <span class="n">OPEN</span><span class="o">.</span><span class="na">length</span><span class="o">());</span>

        <span class="c1">//判断是否有嵌套
</span><span class="c1"></span>        <span class="c1">//${some_${other}_key}
</span><span class="c1"></span>        <span class="k">while</span> <span class="o">(</span><span class="n">nestedStartIndex</span> <span class="o">&gt;</span> <span class="o">-</span><span class="n">1</span> <span class="o">&amp;&amp;</span> <span class="n">markerEndIndex</span> <span class="o">&gt;</span> <span class="o">-</span><span class="n">1</span> <span class="o">&amp;&amp;</span> <span class="n">nestedStartIndex</span> <span class="o">&lt;</span> <span class="n">markerEndIndex</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">nestedStartIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">OPEN</span><span class="o">,</span> <span class="n">nestedStartIndex</span> <span class="o">+</span> <span class="n">OPEN</span><span class="o">.</span><span class="na">length</span><span class="o">());</span>
            <span class="n">markerEndIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">CLOSE</span><span class="o">,</span> <span class="n">markerEndIndex</span> <span class="o">+</span> <span class="n">CLOSE</span><span class="o">.</span><span class="na">length</span><span class="o">());</span>
        <span class="o">}</span>

        <span class="c1">// we have a valid property marker...
</span><span class="c1"></span>        <span class="n">String</span> <span class="n">property</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">markerStartIndex</span> <span class="o">+</span> <span class="n">OPEN</span><span class="o">.</span><span class="na">length</span><span class="o">(),</span> <span class="n">markerEndIndex</span><span class="o">);</span>
        <span class="n">String</span> <span class="n">propertyValue</span> <span class="o">=</span> <span class="n">resolveProperty</span><span class="o">(</span><span class="n">parsePropertyTokens</span><span class="o">(</span><span class="n">property</span><span class="o">));</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">propertyValue</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
            <span class="c1">// add the property marker back into the stream
</span><span class="c1"></span>            <span class="n">answer</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">markerStartIndex</span><span class="o">,</span> <span class="n">markerEndIndex</span> <span class="o">+</span> <span class="n">1</span><span class="o">));</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="n">answer</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">propertyValue</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="n">currentIndex</span> <span class="o">=</span> <span class="n">markerEndIndex</span> <span class="o">+</span> <span class="n">CLOSE</span><span class="o">.</span><span class="na">length</span><span class="o">();</span>
        <span class="n">markerStartIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">OPEN</span><span class="o">,</span> <span class="n">currentIndex</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">//处理剩余的字符
</span><span class="c1"></span>    <span class="k">if</span> <span class="o">(</span><span class="n">currentIndex</span> <span class="o">&lt;</span> <span class="n">s</span><span class="o">.</span><span class="na">length</span><span class="o">())</span> <span class="o">{</span>
        <span class="n">answer</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">currentIndex</span><span class="o">));</span>
    <span class="o">}</span>

    <span class="k">return</span> <span class="n">answer</span><span class="o">.</span><span class="na">stream</span><span class="o">().</span><span class="na">collect</span><span class="o">(</span><span class="n">Collectors</span><span class="o">.</span><span class="na">joining</span><span class="o">());</span>
<span class="o">}</span>
</code></pre></div></li>
<li>
<p>解析xml-element属性</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="kd">protected</span> <span class="n">Properties</span> <span class="nf">parseAttributes</span><span class="o">(</span><span class="n">Node</span> <span class="n">node</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">Properties</span> <span class="n">attributes</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Properties</span><span class="o">();</span>
    <span class="n">NamedNodeMap</span> <span class="n">nnm</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="na">getAttributes</span><span class="o">();</span>
    <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">nnm</span><span class="o">.</span><span class="na">getLength</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
        <span class="n">Node</span> <span class="n">attribute</span> <span class="o">=</span> <span class="n">nnm</span><span class="o">.</span><span class="na">item</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
        <span class="n">String</span> <span class="n">value</span> <span class="o">=</span> <span class="n">parsePropertyTokens</span><span class="o">(</span><span class="n">attribute</span><span class="o">.</span><span class="na">getNodeValue</span><span class="o">());</span>
        <span class="n">attributes</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">attribute</span><span class="o">.</span><span class="na">getNodeName</span><span class="o">(),</span> <span class="n">value</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="k">return</span> <span class="n">attributes</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></li>
</ol>
<h3 id="生成代码">生成代码</h3>
<blockquote>
<p>MyBatisGenerator</p>
</blockquote>
<h4 id="主流程-2">主流程</h4>
<ol>
<li>创建<code>MyBatisGenerator</code>对象，构造函数中执行<code>configuration.validate()</code>方法
<ol>
<li>检查classPathEntry</li>
<li>检查Context，主要检查参数复制是否合法（如不能为空），过程中创建了<code>IntrospectedTable</code>对象</li>
</ol>
</li>
<li>执行<code>MyBatisGenerator#generate</code>方法，执行结果会放入构造函数中的     <code>List&lt;String&gt; warnings</code>参数中，执行过程中，通过回调<code>ProgressCallback</code>相关方法，通知调用方执行进度
<ol>
<li>找到待处理的<code>Context</code>（<code>contextsToRun</code>）</li>
<li>加载自定义类加载器</li>
<li><code>context.introspectTables</code>连接到数据源，解析表结构，并与配置文件进行匹配</li>
<li><code>context.generateFiles</code>基于<code>introspectedTable</code>生成java、xml和kotlin代码（插件机制）</li>
<li>将生成的代码写入文件</li>
<li>可通过<code>shellCallback#refreshProject</code>告知IDE文件已生成，由IDE刷新项目</li>
</ol>
</li>
</ol>
<h4 id="知识点-2">知识点</h4>
<ol>
<li>
<p>执行进度监控（<code>ProgressCallback</code>)</p>
<blockquote>
<p>抽象出各个执行阶段（phase）；<br>
易于统计的总执行步骤数，如：对于每个Context，步骤数=连接数据库 + 数据表数量*1；</p>
</blockquote>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">ProgressCallback</span> <span class="o">{</span>
    <span class="cm">/**
</span><span class="cm">    * Called to note the start of the introspection phase, and to note the
</span><span class="cm">    * maximum number of startTask messages that will be sent for the
</span><span class="cm">    * introspection phase.
</span><span class="cm">    *
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">introspectionStarted</span><span class="o">(</span><span class="kt">int</span> <span class="n">totalTasks</span><span class="o">);</span>

    <span class="cm">/**
</span><span class="cm">    * Called to note the start of the generation phase, and to note the maximum
</span><span class="cm">    * number of startTask messages that will be sent for the generation phase.
</span><span class="cm">    *
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">generationStarted</span><span class="o">(</span><span class="kt">int</span> <span class="n">totalTasks</span><span class="o">);</span>

    <span class="cm">/**
</span><span class="cm">    * Called to note the start of the file saving phase, and to note the
</span><span class="cm">    * maximum number of startTask messages that will be sent for the file
</span><span class="cm">    * saving phase phase.
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">saveStarted</span><span class="o">(</span><span class="kt">int</span> <span class="n">totalTasks</span><span class="o">);</span>

    <span class="cm">/**
</span><span class="cm">    * Called to denote the beginning of a save task.
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">startTask</span><span class="o">(</span><span class="n">String</span> <span class="n">taskName</span><span class="o">);</span>

    <span class="cm">/**
</span><span class="cm">    * This method is called when all generated files have been saved.
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">done</span><span class="o">();</span>

    <span class="cm">/**
</span><span class="cm">    * The method is called periodically during a long running method.
</span><span class="cm">    * If the the implementation throws &lt;code&gt;InterruptedException&lt;/code&gt; then
</span><span class="cm">    * the method will be canceled. 
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">checkCancel</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">InterruptedException</span><span class="o">;</span>
<span class="o">}</span>

</code></pre></div></li>
</ol>
<h2 id="maven-wrapper">maven wrapper</h2>
<h3 id="安装maven-wrapper">安装maven-wrapper</h3>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="c1"># 使用阿里镜像，安装完成后，在我的mbp15运行maven命令，会报HTTPS证书验证异常</span>
<span class="c1"># 需再次执行一遍 mvn -N io.takari:maven:0.7.7:wrapper -Dmaven=3.6.0</span>
<span class="c1"># 第二次执行，不可使用MVNW_REPOURL环境变量</span>
<span class="nb">export</span> <span class="nv">MVNW_VERBOSE</span><span class="o">=</span><span class="nb">true</span>
<span class="nb">export</span> <span class="nv">MVNW_REPOURL</span><span class="o">=</span>https://maven.aliyun.com/repository/public

mvn -N io.takari:maven:0.7.7:wrapper -Dmaven<span class="o">=</span>3.6.0

<span class="c1"># 生成maven编译脚本</span>
<span class="nv">MAVEN_OPTS</span><span class="o">=</span><span class="s2">&#34;-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true&#34;</span>

<span class="nv">scripts</span><span class="o">=(</span>package install deploy<span class="o">)</span>

<span class="k">for</span> script in <span class="si">${</span><span class="nv">scripts</span><span class="p">[@]</span><span class="si">}</span><span class="p">;</span>
<span class="k">do</span>
  rm -f <span class="nv">$script</span>.sh
  <span class="nb">echo</span> <span class="s2">&#34;export MVNW_VERBOSE=true\nexport MVNW_REPOURL=https://maven.aliyun.com/repository/public\n\n./mvnw clean </span><span class="nv">$script</span><span class="s2"> </span><span class="nv">$MAVEN_OPTS</span><span class="s2">&#34;</span> &gt; <span class="nv">$script</span>.sh
  chmod a+x <span class="nv">$script</span>.sh
<span class="k">done</span>

</code></pre></div><h3 id="使用阿里云镜像">使用阿里云镜像</h3>
<div class="highlight"><pre class="chroma"><code class="language-xml" data-lang="xml"><span class="c">&lt;!-- pom中增加以下配置 --&gt;</span>
<span class="nt">&lt;repositories&gt;</span>
    <span class="nt">&lt;repository&gt;</span>
        <span class="nt">&lt;id&gt;</span>central<span class="nt">&lt;/id&gt;</span>
        <span class="nt">&lt;url&gt;</span>https://maven.aliyun.com/repository/public<span class="nt">&lt;/url&gt;</span>
        <span class="nt">&lt;releases&gt;</span>
            <span class="nt">&lt;enabled&gt;</span>true<span class="nt">&lt;/enabled&gt;</span>
        <span class="nt">&lt;/releases&gt;</span>
        <span class="nt">&lt;snapshots&gt;</span>
            <span class="nt">&lt;enabled&gt;</span>false<span class="nt">&lt;/enabled&gt;</span>
        <span class="nt">&lt;/snapshots&gt;</span>
    <span class="nt">&lt;/repository&gt;</span>
<span class="nt">&lt;/repositories&gt;</span>
<span class="nt">&lt;pluginRepositories&gt;</span>
    <span class="nt">&lt;pluginRepository&gt;</span>
        <span class="nt">&lt;id&gt;</span>central<span class="nt">&lt;/id&gt;</span>
        <span class="nt">&lt;url&gt;</span>https://maven.aliyun.com/repository/public<span class="nt">&lt;/url&gt;</span>
        <span class="nt">&lt;releases&gt;</span>
            <span class="nt">&lt;enabled&gt;</span>true<span class="nt">&lt;/enabled&gt;</span>
        <span class="nt">&lt;/releases&gt;</span>
        <span class="nt">&lt;snapshots&gt;</span>
            <span class="nt">&lt;enabled&gt;</span>false<span class="nt">&lt;/enabled&gt;</span>
        <span class="nt">&lt;/snapshots&gt;</span>
    <span class="nt">&lt;/pluginRepository&gt;</span>
<span class="nt">&lt;/pluginRepositories&gt;</span>
</code></pre></div><div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="nb">export</span> <span class="nv">MVNW_VERBOSE</span><span class="o">=</span><span class="nb">true</span>
<span class="nb">export</span> <span class="nv">MVNW_REPOURL</span><span class="o">=</span>https://maven.aliyun.com/repository/public

<span class="c1"># 如果执行maven命令时，报HTTPS证书校验失败的异常，可在此使用-Dmaven.wagon.*参数，忽略证书</span>
./mvnw clean package -Dmaven.wagon.http.ssl.insecure<span class="o">=</span><span class="nb">true</span> -Dmaven.wagon.http.ssl.allowall<span class="o">=</span><span class="nb">true</span>
</code></pre></div><h1 id="mybatis-generator-源码笔记-1">MyBatis Generator 源码笔记</h1>
<h2 id="核心功能-1">核心功能</h2>
<h3 id="入口-1">入口</h3>
<blockquote>
<p>org.mybatis.generator.api.ShellRunner#main</p>
</blockquote>
<h4 id="主流程-3">主流程</h4>
<ol>
<li>解析args入参，得到命令行的输入参数</li>
<li>通过<code>ConfigurationParser</code>类解析xml配置文件</li>
<li>通过<code>MyBatisGenerator</code>生成代码</li>
</ol>
<h4 id="知识点-3">知识点</h4>
<ol>
<li>
<p>可参考<code>parseCommandLine</code>方法，实现args解析的功能</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="kd">private</span> <span class="kd">static</span> <span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">&gt;</span> <span class="nf">parseCommandLine</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">errors</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">&lt;&gt;();</span>
    <span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="o">,</span> <span class="n">String</span><span class="o">&gt;</span> <span class="n">arguments</span> <span class="o">=</span> <span class="k">new</span> <span class="n">HashMap</span><span class="o">&lt;&gt;();</span>

    <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">args</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">CONFIG_FILE</span><span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">]))</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">((</span><span class="n">i</span> <span class="o">+</span> <span class="n">1</span><span class="o">)</span> <span class="o">&lt;</span> <span class="n">args</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">arguments</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">CONFIG_FILE</span><span class="o">,</span> <span class="n">args</span><span class="o">[</span><span class="n">i</span> <span class="o">+</span> <span class="n">1</span><span class="o">]);</span>
            <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
                <span class="n">errors</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">getString</span><span class="o">(</span>
                        <span class="s">&#34;RuntimeError.19&#34;</span><span class="o">,</span> <span class="n">CONFIG_FILE</span><span class="o">));</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>            <span class="o">}</span>
            <span class="n">i</span><span class="o">++;</span>
        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">VERBOSE</span><span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">]))</span> <span class="o">{</span>
            <span class="n">arguments</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">VERBOSE</span><span class="o">,</span> <span class="s">&#34;Y&#34;</span><span class="o">);</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">HELP_1</span><span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">]))</span> <span class="o">{</span>
            <span class="n">arguments</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">HELP_1</span><span class="o">,</span> <span class="s">&#34;Y&#34;</span><span class="o">);</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>        <span class="o">}</span> <span class="k">else</span> <span class="k">if</span> <span class="o">(</span><span class="n">FORCE_JAVA_LOGGING</span><span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">]))</span> <span class="o">{</span>
            <span class="n">LogFactory</span><span class="o">.</span><span class="na">forceJavaLogging</span><span class="o">();</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="n">errors</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">getString</span><span class="o">(</span><span class="s">&#34;RuntimeError.20&#34;</span><span class="o">,</span> <span class="n">args</span><span class="o">[</span><span class="n">i</span><span class="o">]));</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>        <span class="o">}</span>
    <span class="o">}</span>

    <span class="k">if</span> <span class="o">(!</span><span class="n">errors</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">())</span> <span class="o">{</span>
        <span class="k">for</span> <span class="o">(</span><span class="n">String</span> <span class="n">error</span> <span class="o">:</span> <span class="n">errors</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">writeLine</span><span class="o">(</span><span class="n">error</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="n">System</span><span class="o">.</span><span class="na">exit</span><span class="o">(-</span><span class="n">1</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="k">return</span> <span class="n">arguments</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></li>
<li>
<p>掌握<code>StringTokenizer</code>常用方法</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="n">StringTokenizer</span> <span class="n">st</span> <span class="o">=</span> <span class="k">new</span> <span class="n">StringTokenizer</span><span class="o">(</span><span class="n">arguments</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">TABLES</span><span class="o">),</span> <span class="s">&#34;,&#34;</span><span class="o">);</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span><span class="k">while</span> <span class="o">(</span><span class="n">st</span><span class="o">.</span><span class="na">hasMoreTokens</span><span class="o">())</span> <span class="o">{</span>
    <span class="n">String</span> <span class="n">s</span> <span class="o">=</span> <span class="n">st</span><span class="o">.</span><span class="na">nextToken</span><span class="o">().</span><span class="na">trim</span><span class="o">();</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">&gt;</span> <span class="n">0</span><span class="o">)</span> <span class="o">{</span>
        <span class="n">fullyqualifiedTables</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">s</span><span class="o">);</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></li>
</ol>
<h3 id="解析配置文件-1">解析配置文件</h3>
<blockquote>
<p>ConfigurationParser</p>
</blockquote>
<h4 id="主流程-4">主流程</h4>
<ol>
<li>创建<code>ConfigurationParser</code>对象，执行<code>parseConfiguration</code>方法</li>
<li>调用<code>MyBatisGeneratorConfigurationParser#parseConfiguration</code>方法</li>
<li>遍历所有xml-element，对<code>properties</code>、<code>classPathEntry</code>、<code>context</code>分别做解析</li>
</ol>
<h4 id="知识点-4">知识点</h4>
<ol>
<li>
<p>XML解析（进阶应用）:dtd验证xml内容、异常处理</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="n">DocumentBuilderFactory</span> <span class="n">factory</span> <span class="o">=</span> <span class="n">DocumentBuilderFactory</span><span class="o">.</span><span class="na">newInstance</span><span class="o">();</span>
<span class="n">factory</span><span class="o">.</span><span class="na">setValidating</span><span class="o">(</span><span class="kc">true</span><span class="o">);</span>
<span class="n">factory</span><span class="o">.</span><span class="na">setFeature</span><span class="o">(</span><span class="n">XMLConstants</span><span class="o">.</span><span class="na">FEATURE_SECURE_PROCESSING</span><span class="o">,</span> <span class="kc">true</span><span class="o">);</span>

<span class="n">DocumentBuilder</span> <span class="n">builder</span> <span class="o">=</span> <span class="n">factory</span><span class="o">.</span><span class="na">newDocumentBuilder</span><span class="o">();</span>
<span class="n">builder</span><span class="o">.</span><span class="na">setEntityResolver</span><span class="o">(</span><span class="k">new</span> <span class="n">ParserEntityResolver</span><span class="o">());</span>

<span class="n">ParserErrorHandler</span> <span class="n">handler</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ParserErrorHandler</span><span class="o">(</span><span class="n">warnings</span><span class="o">,</span>
        <span class="n">parseErrors</span><span class="o">);</span>
<span class="n">builder</span><span class="o">.</span><span class="na">setErrorHandler</span><span class="o">(</span><span class="n">handler</span><span class="o">);</span>

<span class="c1">//use dtd to verify entity
</span><span class="c1"></span><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ParserEntityResolver</span> <span class="kd">implements</span> <span class="n">EntityResolver</span> <span class="o">{</span>
    <span class="nd">@Override</span>
    <span class="kd">public</span> <span class="n">InputSource</span> <span class="nf">resolveEntity</span><span class="o">(</span><span class="n">String</span> <span class="n">publicId</span><span class="o">,</span> <span class="n">String</span> <span class="n">systemId</span><span class="o">)</span>
            <span class="kd">throws</span> <span class="n">SAXException</span><span class="o">,</span> <span class="n">IOException</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">XmlConstants</span><span class="o">.</span><span class="na">MYBATIS_GENERATOR_CONFIG_PUBLIC_ID</span>
                <span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">publicId</span><span class="o">))</span> <span class="o">{</span>
            <span class="n">InputStream</span> <span class="n">is</span> <span class="o">=</span> <span class="n">getClass</span><span class="o">()</span>
                    <span class="o">.</span><span class="na">getClassLoader</span><span class="o">()</span>
                    <span class="o">.</span><span class="na">getResourceAsStream</span><span class="o">(</span>
                            <span class="s">&#34;org/mybatis/generator/config/xml/mybatis-generator-config_1_0.dtd&#34;</span><span class="o">);</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>            <span class="k">return</span> <span class="k">new</span> <span class="n">InputSource</span><span class="o">(</span><span class="n">is</span><span class="o">);</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></li>
<li>
<p>通过classLoader读取类路径内的资源，通过文件路径（url）读取本地路径的文件</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"> <span class="n">URL</span> <span class="n">resourceUrl</span> <span class="o">=</span> <span class="n">ObjectFactory</span><span class="o">.</span><span class="na">class</span><span class="o">.</span><span class="na">getClassLoader</span><span class="o">().</span><span class="na">getResource</span><span class="o">(</span><span class="n">resource</span><span class="o">);</span>
 <span class="n">URL</span> <span class="n">fileUrl</span> <span class="o">=</span> <span class="k">new</span> <span class="n">URL</span><span class="o">(</span><span class="n">url</span><span class="o">);</span>
</code></pre></div></li>
<li>
<p>解析带有${KEY}的表达式</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="c1">// 代码有所简化，不可直接使用
</span><span class="c1"></span><span class="n">String</span> <span class="nf">parsePropertyTokens</span><span class="o">(</span><span class="n">String</span> <span class="n">s</span><span class="o">)</span> <span class="o">{</span>
    <span class="kd">final</span> <span class="n">String</span> <span class="n">OPEN</span> <span class="o">=</span> <span class="s">&#34;${&#34;</span><span class="o">;</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>    <span class="kd">final</span> <span class="n">String</span> <span class="n">CLOSE</span> <span class="o">=</span> <span class="s">&#34;}&#34;</span><span class="o">;</span> <span class="c1">//$NON-NLS-1$
</span><span class="c1"></span>    <span class="kt">int</span> <span class="n">currentIndex</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span>
    <span class="n">List</span><span class="o">&lt;</span><span class="n">String</span><span class="o">&gt;</span> <span class="n">answer</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ArrayList</span><span class="o">&lt;&gt;();</span>

    <span class="kt">int</span> <span class="n">markerStartIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">OPEN</span><span class="o">);</span>
    <span class="k">while</span> <span class="o">(</span><span class="n">markerStartIndex</span> <span class="o">&gt;</span> <span class="o">-</span><span class="n">1</span><span class="o">)</span> <span class="o">{</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">markerStartIndex</span> <span class="o">&gt;</span> <span class="n">currentIndex</span><span class="o">)</span> <span class="o">{</span>
            <span class="c1">// add the characters before the next parameter marker
</span><span class="c1"></span>            <span class="n">answer</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">currentIndex</span><span class="o">,</span> <span class="n">markerStartIndex</span><span class="o">));</span>
            <span class="n">currentIndex</span> <span class="o">=</span> <span class="n">markerStartIndex</span><span class="o">;</span>
        <span class="o">}</span>

        <span class="kt">int</span> <span class="n">markerEndIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">CLOSE</span><span class="o">,</span> <span class="n">currentIndex</span><span class="o">);</span>
        <span class="kt">int</span> <span class="n">nestedStartIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">OPEN</span><span class="o">,</span> <span class="n">markerStartIndex</span> <span class="o">+</span> <span class="n">OPEN</span><span class="o">.</span><span class="na">length</span><span class="o">());</span>

        <span class="c1">//判断是否有嵌套
</span><span class="c1"></span>        <span class="c1">//${some_${other}_key}
</span><span class="c1"></span>        <span class="k">while</span> <span class="o">(</span><span class="n">nestedStartIndex</span> <span class="o">&gt;</span> <span class="o">-</span><span class="n">1</span> <span class="o">&amp;&amp;</span> <span class="n">markerEndIndex</span> <span class="o">&gt;</span> <span class="o">-</span><span class="n">1</span> <span class="o">&amp;&amp;</span> <span class="n">nestedStartIndex</span> <span class="o">&lt;</span> <span class="n">markerEndIndex</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">nestedStartIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">OPEN</span><span class="o">,</span> <span class="n">nestedStartIndex</span> <span class="o">+</span> <span class="n">OPEN</span><span class="o">.</span><span class="na">length</span><span class="o">());</span>
            <span class="n">markerEndIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">CLOSE</span><span class="o">,</span> <span class="n">markerEndIndex</span> <span class="o">+</span> <span class="n">CLOSE</span><span class="o">.</span><span class="na">length</span><span class="o">());</span>
        <span class="o">}</span>

        <span class="c1">// we have a valid property marker...
</span><span class="c1"></span>        <span class="n">String</span> <span class="n">property</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">markerStartIndex</span> <span class="o">+</span> <span class="n">OPEN</span><span class="o">.</span><span class="na">length</span><span class="o">(),</span> <span class="n">markerEndIndex</span><span class="o">);</span>
        <span class="n">String</span> <span class="n">propertyValue</span> <span class="o">=</span> <span class="n">resolveProperty</span><span class="o">(</span><span class="n">parsePropertyTokens</span><span class="o">(</span><span class="n">property</span><span class="o">));</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">propertyValue</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
            <span class="c1">// add the property marker back into the stream
</span><span class="c1"></span>            <span class="n">answer</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">markerStartIndex</span><span class="o">,</span> <span class="n">markerEndIndex</span> <span class="o">+</span> <span class="n">1</span><span class="o">));</span>
        <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
            <span class="n">answer</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">propertyValue</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="n">currentIndex</span> <span class="o">=</span> <span class="n">markerEndIndex</span> <span class="o">+</span> <span class="n">CLOSE</span><span class="o">.</span><span class="na">length</span><span class="o">();</span>
        <span class="n">markerStartIndex</span> <span class="o">=</span> <span class="n">s</span><span class="o">.</span><span class="na">indexOf</span><span class="o">(</span><span class="n">OPEN</span><span class="o">,</span> <span class="n">currentIndex</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="c1">//处理剩余的字符
</span><span class="c1"></span>    <span class="k">if</span> <span class="o">(</span><span class="n">currentIndex</span> <span class="o">&lt;</span> <span class="n">s</span><span class="o">.</span><span class="na">length</span><span class="o">())</span> <span class="o">{</span>
        <span class="n">answer</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">s</span><span class="o">.</span><span class="na">substring</span><span class="o">(</span><span class="n">currentIndex</span><span class="o">));</span>
    <span class="o">}</span>

    <span class="k">return</span> <span class="n">answer</span><span class="o">.</span><span class="na">stream</span><span class="o">().</span><span class="na">collect</span><span class="o">(</span><span class="n">Collectors</span><span class="o">.</span><span class="na">joining</span><span class="o">());</span>
<span class="o">}</span>
</code></pre></div></li>
<li>
<p>解析xml-element属性</p>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="kd">protected</span> <span class="n">Properties</span> <span class="nf">parseAttributes</span><span class="o">(</span><span class="n">Node</span> <span class="n">node</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">Properties</span> <span class="n">attributes</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Properties</span><span class="o">();</span>
    <span class="n">NamedNodeMap</span> <span class="n">nnm</span> <span class="o">=</span> <span class="n">node</span><span class="o">.</span><span class="na">getAttributes</span><span class="o">();</span>
    <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">nnm</span><span class="o">.</span><span class="na">getLength</span><span class="o">();</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
        <span class="n">Node</span> <span class="n">attribute</span> <span class="o">=</span> <span class="n">nnm</span><span class="o">.</span><span class="na">item</span><span class="o">(</span><span class="n">i</span><span class="o">);</span>
        <span class="n">String</span> <span class="n">value</span> <span class="o">=</span> <span class="n">parsePropertyTokens</span><span class="o">(</span><span class="n">attribute</span><span class="o">.</span><span class="na">getNodeValue</span><span class="o">());</span>
        <span class="n">attributes</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">attribute</span><span class="o">.</span><span class="na">getNodeName</span><span class="o">(),</span> <span class="n">value</span><span class="o">);</span>
    <span class="o">}</span>

    <span class="k">return</span> <span class="n">attributes</span><span class="o">;</span>
<span class="o">}</span>
</code></pre></div></li>
</ol>
<h3 id="生成代码-1">生成代码</h3>
<blockquote>
<p>MyBatisGenerator</p>
</blockquote>
<h4 id="主流程-5">主流程</h4>
<ol>
<li>创建<code>MyBatisGenerator</code>对象，构造函数中执行<code>configuration.validate()</code>方法
<ol>
<li>检查classPathEntry</li>
<li>检查Context，主要检查参数复制是否合法（如不能为空），过程中创建了<code>IntrospectedTable</code>对象</li>
</ol>
</li>
<li>执行<code>MyBatisGenerator#generate</code>方法，执行结果会放入构造函数中的     <code>List&lt;String&gt; warnings</code>参数中，执行过程中，通过回调<code>ProgressCallback</code>相关方法，通知调用方执行进度
<ol>
<li>找到待处理的<code>Context</code>（<code>contextsToRun</code>）</li>
<li>加载自定义类加载器</li>
<li><code>context.introspectTables</code>连接到数据源，解析表结构，并与配置文件进行匹配</li>
<li><code>context.generateFiles</code>基于<code>introspectedTable</code>生成java、xml和kotlin代码（插件机制）</li>
<li>将生成的代码写入文件</li>
<li>可通过<code>shellCallback#refreshProject</code>告知IDE文件已生成，由IDE刷新项目</li>
</ol>
</li>
</ol>
<h4 id="知识点-5">知识点</h4>
<ol>
<li>
<p>执行进度监控（<code>ProgressCallback</code>)</p>
<blockquote>
<p>抽象出各个执行阶段（phase）；<br>
易于统计的总执行步骤数，如：对于每个Context，步骤数=连接数据库 + 数据表数量*1；</p>
</blockquote>
<div class="highlight"><pre class="chroma"><code class="language-java" data-lang="java"><span class="kd">public</span> <span class="kd">interface</span> <span class="nc">ProgressCallback</span> <span class="o">{</span>
    <span class="cm">/**
</span><span class="cm">    * Called to note the start of the introspection phase, and to note the
</span><span class="cm">    * maximum number of startTask messages that will be sent for the
</span><span class="cm">    * introspection phase.
</span><span class="cm">    *
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">introspectionStarted</span><span class="o">(</span><span class="kt">int</span> <span class="n">totalTasks</span><span class="o">);</span>

    <span class="cm">/**
</span><span class="cm">    * Called to note the start of the generation phase, and to note the maximum
</span><span class="cm">    * number of startTask messages that will be sent for the generation phase.
</span><span class="cm">    *
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">generationStarted</span><span class="o">(</span><span class="kt">int</span> <span class="n">totalTasks</span><span class="o">);</span>

    <span class="cm">/**
</span><span class="cm">    * Called to note the start of the file saving phase, and to note the
</span><span class="cm">    * maximum number of startTask messages that will be sent for the file
</span><span class="cm">    * saving phase phase.
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">saveStarted</span><span class="o">(</span><span class="kt">int</span> <span class="n">totalTasks</span><span class="o">);</span>

    <span class="cm">/**
</span><span class="cm">    * Called to denote the beginning of a save task.
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">startTask</span><span class="o">(</span><span class="n">String</span> <span class="n">taskName</span><span class="o">);</span>

    <span class="cm">/**
</span><span class="cm">    * This method is called when all generated files have been saved.
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">done</span><span class="o">();</span>

    <span class="cm">/**
</span><span class="cm">    * The method is called periodically during a long running method.
</span><span class="cm">    * If the the implementation throws &lt;code&gt;InterruptedException&lt;/code&gt; then
</span><span class="cm">    * the method will be canceled. 
</span><span class="cm">    */</span>
    <span class="kt">void</span> <span class="nf">checkCancel</span><span class="o">()</span> <span class="kd">throws</span> <span class="n">InterruptedException</span><span class="o">;</span>
<span class="o">}</span>

</code></pre></div></li>
</ol>
<h2 id="maven-wrapper-1">maven wrapper</h2>
<h3 id="安装maven-wrapper-1">安装maven-wrapper</h3>
<div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="c1"># 使用阿里镜像，安装完成后，在我的mbp15运行maven命令，会报HTTPS证书验证异常</span>
<span class="c1"># 需再次执行一遍 mvn -N io.takari:maven:0.7.7:wrapper -Dmaven=3.6.0</span>
<span class="c1"># 第二次执行，不可使用MVNW_REPOURL环境变量</span>
<span class="nb">export</span> <span class="nv">MVNW_VERBOSE</span><span class="o">=</span><span class="nb">true</span>
<span class="nb">export</span> <span class="nv">MVNW_REPOURL</span><span class="o">=</span>https://maven.aliyun.com/repository/public

mvn -N io.takari:maven:0.7.7:wrapper -Dmaven<span class="o">=</span>3.6.0

<span class="c1"># 生成maven编译脚本</span>
<span class="nv">MAVEN_OPTS</span><span class="o">=</span><span class="s2">&#34;-Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true&#34;</span>

<span class="nv">scripts</span><span class="o">=(</span>package install deploy<span class="o">)</span>

<span class="k">for</span> script in <span class="si">${</span><span class="nv">scripts</span><span class="p">[@]</span><span class="si">}</span><span class="p">;</span>
<span class="k">do</span>
  rm -f <span class="nv">$script</span>.sh
  <span class="nb">echo</span> <span class="s2">&#34;export MVNW_VERBOSE=true\nexport MVNW_REPOURL=https://maven.aliyun.com/repository/public\n\n./mvnw clean </span><span class="nv">$script</span><span class="s2"> </span><span class="nv">$MAVEN_OPTS</span><span class="s2">&#34;</span> &gt; <span class="nv">$script</span>.sh
  chmod a+x <span class="nv">$script</span>.sh
<span class="k">done</span>

</code></pre></div><h3 id="使用阿里云镜像-1">使用阿里云镜像</h3>
<div class="highlight"><pre class="chroma"><code class="language-xml" data-lang="xml"><span class="c">&lt;!-- pom中增加以下配置 --&gt;</span>
<span class="nt">&lt;repositories&gt;</span>
    <span class="nt">&lt;repository&gt;</span>
        <span class="nt">&lt;id&gt;</span>central<span class="nt">&lt;/id&gt;</span>
        <span class="nt">&lt;url&gt;</span>https://maven.aliyun.com/repository/public<span class="nt">&lt;/url&gt;</span>
        <span class="nt">&lt;releases&gt;</span>
            <span class="nt">&lt;enabled&gt;</span>true<span class="nt">&lt;/enabled&gt;</span>
        <span class="nt">&lt;/releases&gt;</span>
        <span class="nt">&lt;snapshots&gt;</span>
            <span class="nt">&lt;enabled&gt;</span>false<span class="nt">&lt;/enabled&gt;</span>
        <span class="nt">&lt;/snapshots&gt;</span>
    <span class="nt">&lt;/repository&gt;</span>
<span class="nt">&lt;/repositories&gt;</span>
<span class="nt">&lt;pluginRepositories&gt;</span>
    <span class="nt">&lt;pluginRepository&gt;</span>
        <span class="nt">&lt;id&gt;</span>central<span class="nt">&lt;/id&gt;</span>
        <span class="nt">&lt;url&gt;</span>https://maven.aliyun.com/repository/public<span class="nt">&lt;/url&gt;</span>
        <span class="nt">&lt;releases&gt;</span>
            <span class="nt">&lt;enabled&gt;</span>true<span class="nt">&lt;/enabled&gt;</span>
        <span class="nt">&lt;/releases&gt;</span>
        <span class="nt">&lt;snapshots&gt;</span>
            <span class="nt">&lt;enabled&gt;</span>false<span class="nt">&lt;/enabled&gt;</span>
        <span class="nt">&lt;/snapshots&gt;</span>
    <span class="nt">&lt;/pluginRepository&gt;</span>
<span class="nt">&lt;/pluginRepositories&gt;</span>
</code></pre></div><div class="highlight"><pre class="chroma"><code class="language-shell" data-lang="shell"><span class="nb">export</span> <span class="nv">MVNW_VERBOSE</span><span class="o">=</span><span class="nb">true</span>
<span class="nb">export</span> <span class="nv">MVNW_REPOURL</span><span class="o">=</span>https://maven.aliyun.com/repository/public

<span class="c1"># 如果执行maven命令时，报HTTPS证书校验失败的异常，可在此使用-Dmaven.wagon.*参数，忽略证书</span>
./mvnw clean package -Dmaven.wagon.http.ssl.insecure<span class="o">=</span><span class="nb">true</span> -Dmaven.wagon.http.ssl.allowall<span class="o">=</span><span class="nb">true</span>
</code></pre></div></div><div class="post-footer" id="post-footer">
    <div class="post-info">
        <div class="post-info-line">
            <div class="post-info-mod">
                <span>Updated on 2020-10-08</span>
            </div>
            <div class="post-info-license"></div>
        </div>
        <div class="post-info-line">
            <div class="post-info-md"></div>
            <div class="post-info-share">
                <span></span>
            </div>
        </div>
    </div>

    <div class="post-info-more">
        <section class="post-tags"><i class="fas fa-tags fa-fw"></i>&nbsp;<a href="/tags/mybatis/">mybatis</a>,&nbsp;<a href="/tags/mbg/">mbg</a>,&nbsp;<a href="/tags/%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB/">源码阅读</a></section>
        <section>
            <span><a href="javascript:void(0);" onclick="window.history.back();">Back</a></span>&nbsp;|&nbsp;<span><a href="/">Home</a></span>
        </section>
    </div>

    <div class="post-nav"><a href="/install_nexus/" class="prev" rel="prev" title="安装maven私服"><i class="fas fa-angle-left fa-fw"></i>安装maven私服</a>
            <a href="/uber-payments-arch/" class="next" rel="next" title="【思考】Uber 下一代支付平台的系统架构设计">【思考】Uber 下一代支付平台的系统架构设计<i class="fas fa-angle-right fa-fw"></i></a></div>
</div>
</article></div>
            </main><footer class="footer">
        <div class="footer-container"><div class="footer-line">Powered by <a href="https://gohugo.io/" target="_blank" rel="noopener noreffer" title="Hugo 0.75.1">Hugo</a> | Theme - <a href="https://github.com/dillonzq/LoveIt" target="_blank" rel="noopener noreffer" title="LoveIt 0.2.10"><i class="far fa-kiss-wink-heart fa-fw"></i> LoveIt</a>
                </div><div class="footer-line"><i class="far fa-copyright fa-fw"></i><span itemprop="copyrightYear">2019 - 2020</span><span class="author" itemprop="copyrightHolder">&nbsp;<a href="/" target="_blank"></a></span>&nbsp;|&nbsp;<span class="license">本文采用<a rel="license" href="http://creativecommons.org/licenses/by-nc/4.0/" target="_blank">知识共享署名-非商业性使用 4.0 国际许可协议</a>进行许可</span></div>
        </div>
    </footer></div>

        <div id="fixed-buttons"><a href="#" id="back-to-top" class="fixed-button" title="Back to Top">
                <i class="fas fa-arrow-up fa-fw"></i>
            </a><a href="#" id="view-comments" class="fixed-button" title="View Comments">
                <i class="fas fa-comment fa-fw"></i>
            </a>
        </div><link rel="stylesheet" href="/lib/lightgallery/lightgallery.min.css"><script type="text/javascript" src="/lib/smooth-scroll/smooth-scroll.min.js"></script><script type="text/javascript" src="/lib/autocomplete/autocomplete.min.js"></script><script type="text/javascript" src="/lib/algoliasearch/algoliasearch-lite.umd.min.js"></script><script type="text/javascript" src="/lib/lazysizes/lazysizes.min.js"></script><script type="text/javascript" src="/lib/lightgallery/lightgallery.min.js"></script><script type="text/javascript" src="/lib/lightgallery/lg-thumbnail.min.js"></script><script type="text/javascript" src="/lib/lightgallery/lg-zoom.min.js"></script><script type="text/javascript" src="/lib/clipboard/clipboard.min.js"></script><script type="text/javascript">window.config={"code":{"copyTitle":"Copy to clipboard","maxShownLines":10},"comment":{},"lightGallery":{"actualSize":false,"exThumbImage":"data-thumbnail","hideBarsDelay":2000,"selector":".lightgallery","speed":400,"thumbContHeight":80,"thumbWidth":80,"thumbnail":true},"search":{"algoliaAppID":"JJRDOPDPLW","algoliaIndex":"amath_blog","algoliaSearchKey":"e529bedc6a3af35b53e2597f1c7550c2","highlightTag":"em","maxResultLength":10,"noResultsFound":"No results found","snippetLength":50,"type":"algolia"}};</script><script type="text/javascript" src="/js/theme.min.js"></script></body>
</html>
